﻿using System.Collections.Generic;
using System;
using System.Text;

namespace Ex03.GarageLogic
{
    /// <summary>
    /// This class is an abstract class for an Fuel
    /// Powered Vehicle
    /// Inherits from Vehicle
    /// </summary>
    public abstract class FuelPoweredVehicle : Vehicle
    {
        public enum eFuelType
        {
            Octan95 = 1,
            Octan96,
            Octan98,
            Soler
        }

        private const string k_FuelTypeKey = "Fuel Type";
        private const string k_FuelCapacityInLitersKey = "Fuel Capacity In Liters";
        private const string k_CurrentFuelAmountInLitersKey = "Current Fuel Amount In Liters";
        private const float k_MinimumFuelAmountInLitres = 0.0f;
        private static readonly Dictionary<string, string> sr_FuelPoweredVehicleForm = null;
        
        private float m_CurrentFuelAmountInLitres = 0.0f;
        
        /// <summary>
        /// This is a static constructor that will initialize the Fuel
        /// Powered Vehicle form 
        /// </summary>
        static FuelPoweredVehicle()
        {
            sr_FuelPoweredVehicleForm = new Dictionary<string, string>(VehicleForm);
            sr_FuelPoweredVehicleForm.Add(k_CurrentFuelAmountInLitersKey, null);
        }

        /// <summary>
        /// A constructor that will pass all input to its base
        /// </summary>
        /// <param name="i_PlateNumber">plate number needed by parent </param>
        /// <param name="i_NumberOfWheels">number of wheels needed by parent</param>
        /// <param name="i_WheelMaxAirPressure">wheel max air pressure needed by parent</param>
        protected FuelPoweredVehicle(string i_PlateNumber, int i_NumberOfWheels, float i_WheelMaxAirPressure)
            : base(i_PlateNumber, i_NumberOfWheels, i_WheelMaxAirPressure)
        {
        }

        /// <summary>
        /// This is the main initialization function of this object
        /// This function will receive a form with all the data needed for the initialization
        /// </summary>
        /// <param name="i_CreationForm">A key value Dictionary where key is the name of the member 
        /// to initialize and value is the new value
        /// </param>
        public override void Initialize(Dictionary<string, string> i_CreationForm)
        {
            base.Initialize(i_CreationForm);
            bool isGetValueSucceded; 
            bool isParseSucceded; 
            string CurrentFuelAmountInLitresString;
            float currentFuelAmountInLitresTemp;

            isGetValueSucceded = i_CreationForm.TryGetValue(k_CurrentFuelAmountInLitersKey, out CurrentFuelAmountInLitresString);
            isParseSucceded = float.TryParse(CurrentFuelAmountInLitresString, out currentFuelAmountInLitresTemp);
            if (!isGetValueSucceded || !isParseSucceded)
            {
                throw new FormatException("Current fuel amount in liters is invalid");
            }

            if (currentFuelAmountInLitresTemp < 0 || currentFuelAmountInLitresTemp > FuelCapacityInLitres)
            {
                throw new ValueOutOfRangeException("Current fuel amount in liters out of range", k_MinimumFuelAmountInLitres, FuelCapacityInLitres);
            }

            m_CurrentFuelAmountInLitres = currentFuelAmountInLitresTemp;   
        }

        /// <summary>
        /// An override property to get the energy left in percent
        /// </summary>
        public override float EnergyLeftInPercent
        {
            get { return (CurrentFuelAmountInLitres / FuelCapacityInLitres) * 100; }
        }

        /// <summary>
        /// An abstract property to get the fuel type
        /// </summary>
        public abstract eFuelType FuelType { get; }

        /// <summary>
        /// An abstract property to get the fuel capacity in liters
        /// </summary>
        public abstract float FuelCapacityInLitres { get; }

        /// <summary>
        /// A property to get the current fuel amount in liters
        /// </summary>
        public float CurrentFuelAmountInLitres
        {
            get { return m_CurrentFuelAmountInLitres; }
        }

        /// <summary>
        /// A property to get the fuel powered vehicle form
        /// <remarks>will return a new form</remarks>
        /// </summary>
        protected static Dictionary<string, string> FuelPoweredVehicleForm
        {
            get { return new Dictionary<string, string>(sr_FuelPoweredVehicleForm); }
        }

        /// <summary>
        /// Allows you to refuel the vehicles
        /// </summary>
        /// <param name="i_AmountInLitres">
        ///     The amount to charge
        ///     <remarks>
        ///         Must be larger then 0 and the sum of current Fuel amount and this value 
        ///         should not excised the tank capacity, in such a case will throw ValueOutOfRangeException
        ///     </remarks>
        /// </param>
        /// <param name="i_FuelType">
        ///     The type of fuel
        ///     <remarks>
        ///         Must be the vehicle's fuel type, if not will throw ArgumentException
        ///     </remarks>
        /// </param>
        public void Refuel(float i_AmountInLitres, eFuelType i_FuelType)
        {
            float targetAmountOfLitresInTank;

            if (FuelType != i_FuelType)
            {
                throw new ArgumentException(string.Format("Wrong fuel type, expected {0} but received {1}", FuelType, i_FuelType));
            }

            targetAmountOfLitresInTank = m_CurrentFuelAmountInLitres + i_AmountInLitres;
            if (targetAmountOfLitresInTank > FuelCapacityInLitres || targetAmountOfLitresInTank < k_MinimumFuelAmountInLitres)
            {
                throw new ValueOutOfRangeException("Tank capacity out of range", k_MinimumFuelAmountInLitres, FuelCapacityInLitres);
            }

            m_CurrentFuelAmountInLitres = targetAmountOfLitresInTank;
        }

        /// <summary>
        /// Return a string representation object members as pair 
        /// members name and variable value
        /// </summary>
        /// <returns>string "varName + Vehicle.k_ToStirngPairDelimiter + varValue + Vehicle.sr_ToStirngEndOfPairDelimiter"</returns>
        public override string ToString()
        {
            StringBuilder stringBuilderResult = new StringBuilder();

            stringBuilderResult.Append(base.ToString());
            stringBuilderResult.Append(fuelTypeToString());
            stringBuilderResult.Append(fuelCapacityInLitersToString());
            stringBuilderResult.Append(currentFuelAmountInLitersToString());

            return stringBuilderResult.ToString();
        }

        private string fuelTypeToString()
        {
            StringBuilder stringBuilderResult = new StringBuilder();

            stringBuilderResult.Append(k_FuelTypeKey);
            stringBuilderResult.Append(Vehicle.k_ToStirngPairDelimiter);
            stringBuilderResult.Append(FuelType);
            stringBuilderResult.Append(Vehicle.sr_ToStirngEndOfPairDelimiter);

            return stringBuilderResult.ToString();
        }

        private string fuelCapacityInLitersToString()
        {
            StringBuilder stringBuilderResult = new StringBuilder();

            stringBuilderResult.Append(k_FuelCapacityInLitersKey);
            stringBuilderResult.Append(Vehicle.k_ToStirngPairDelimiter);
            stringBuilderResult.Append(FuelCapacityInLitres);
            stringBuilderResult.Append(Vehicle.sr_ToStirngEndOfPairDelimiter);

            return stringBuilderResult.ToString();
        }

        private string currentFuelAmountInLitersToString()
        {
            StringBuilder stringBuilderResult = new StringBuilder();

            stringBuilderResult.Append(k_CurrentFuelAmountInLitersKey);
            stringBuilderResult.Append(Vehicle.k_ToStirngPairDelimiter);
            stringBuilderResult.Append(m_CurrentFuelAmountInLitres);
            stringBuilderResult.Append(Vehicle.sr_ToStirngEndOfPairDelimiter);

            return stringBuilderResult.ToString();
        }
    }
}
